using System;
using System.Collections;
using System.ComponentModel;
using System.Reflection;
using Common.Core;
using Common.Unity.Extensions.PropertyMapper.Policies;
using Microsoft.Practices.ObjectBuilder2;
using Common.Unity.Extensions;

namespace Common.Unity.Extensions.PropertyMapper
{
	class CollectionPropertyMappingExpressionPropertySetterInfo : PropertyMappingExpressionPropertySetterInfo
	{
		readonly NamedTypeBuildKey itemBuildKey;

		public CollectionPropertyMappingExpressionPropertySetterInfo( PropertyMappingExpressionContext sourceContext,
		                                                              PropertyMappingExpressionContext targetContext,
		                                                              NamedTypeBuildKey itemBuildKey )
			: base( sourceContext, targetContext, null )
		{
			this.itemBuildKey = itemBuildKey;
		}

		protected override void Set( IBuilderContext context, object instance, object buildKey)
		{
			IList
				target = ResolveCollection( TargetContext.Value ),
				source = EnsureCollection( context, itemBuildKey, ResolveCollection( SourceContext.Value ), ListHelper.ResolveElementType( target.GetType() ) );
			ListHelper.Synchronize( source, target );
		}

		static IList EnsureCollection( IBuilderContext context, NamedTypeBuildKey key, IList source, Type type )
		{
			Type sourceType = ListHelper.ResolveElementType( source.GetType() );
			if ( !type.IsAssignableFrom( sourceType ) )
			{
				ArrayList result = new ArrayList();
				foreach ( object item in source )
				{
					IBuilderContext clone = context.CloneForNewBuild( key, null );
					clone.SetBuildSource( item );
					object child = clone.Strategies.ExecuteBuildUp( clone );
					if ( child != null && type.IsAssignableFrom( child.GetType() ) )
					{
						result.Add( child );
					}
				}
				return result;
			}
			return source;
		}

		static IList ResolveCollection( object target )
		{
			IListSource source = target as IListSource;
			if ( source != null )
			{
				return source.GetList();
			}
			IList list = target as IList;
			if ( list != null )
			{
				return list;
			}
			ICollection collection = target as ICollection;
			if ( collection != null )
			{
				return new ArrayList( collection );
			}
			throw new InvalidOperationException( string.Format( "Could not resolve a collection from target of type '{0}'", target.GetType() ) );
		}
	}
}